<!DOCTYPE html>
<html>
<head>
  <title>time control • anime.js</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta property="og:title" content="anime.js">
  <meta property="og:url" content="https://animejs.com">
  <meta property="og:description" content="Javascript Animation Engine">
  <meta property="og:image" content="https://animejs.com/documentation/assets/img/icons/og.png">
  <meta name="twitter:card" content="summary">
  <meta name="twitter:title" content="anime.js">
  <meta name="twitter:site" content="@juliangarnier">
  <meta name="twitter:description" content="Javascript Animation Engine">
  <meta name="twitter:image" content="https://animejs.com/documentation/assets/img/icons/twitter.png">
  <link rel="apple-touch-icon-precomposed" href="../assets/img/social-media-image.png">
  <link rel="icon" type="image/png" href="../assets/img/favicon.png" >
  <link href="../assets/css/animejs.css" rel="stylesheet">
  <style>

    :root {
      font-size: 24px;
    }

    body {
      display: flex;
      flex-wrap: wrap;
      justify-content: center;
      align-items: center;
    }

    .feature-section {
      position: relative;
      display: flex;
    }

    .feature-section-center {
      flex-direction: column;
      align-items: center;
      text-align: center;
    }

    .section-title {
      font-size: 64px;
      line-height: 56px;
      letter-spacing: 0;
      margin-bottom: 1rem;
    }

    .section-title span {
      display: inline-block;
    }

    .section-description {
      font-size: 24px;
      line-height: 32px;
      letter-spacing: 0;
      margin-bottom: 3rem;
    }

    .feature-anim {
      position: relative;
      display: flex;
      flex-wrap: wrap;
      align-items: center;
    }

    .time-control {
      height: 12rem;
    }

    .time-control .ruller {
      position: absolute;
      top: 0;
      display: flex;
      width: calc(34rem + 4px);
      padding: 0 1px;
    }

    .time-control .line {
      width: 2px;
      height: .5rem;
      background-color: currentColor;
    }

    .time-control .line:nth-child(9n+2) {
      height: 1rem;
    }

    .time-control .line:not(:last-child) {
      margin-right: 4px;
    }

    .time-cursor {
      position: absolute;
      z-index: 1;
      top: -1.125rem;
      left: calc(-1rem + 2px);
      width: 2rem;
      height: 2rem;
      background-color: currentColor;
      border-radius: 50%;
    }

    .time-cursor input {
      position: relative;
      z-index: 1;
      width: 2rem;
      height: 2rem;
      color: #FFF;
      background-color: transparent;
      border-radius: 50%;
      text-align: center;
      font-size: .5rem;
    }

    .time-cursor:before {
      content: "";
      position: absolute;
      top: 0;
      left: calc(1rem - 1px);
      display: block;
      width: 2px;
      height: 12.125rem;
      background-color: #F64E4D;
    }

    .timeline {
      position: relative;
      z-index: 1;
      overflow: visible;
      display: flex;
      width: 100%;
      height: 8px;
      font-size: .75rem;
    }

    .animation {
      position: relative;
      overflow: visible;
      display: flex;
    }

    .animation-band {
      position: relative;
      height: 8px;
      margin: 0 1px;
      border-radius: 4px;
    }

    .animation-delay {
      background-color: currentColor;
      width: 48px;
    }
    .animation-change {
      background-color: #F64E4D;
      width: 156px;
    }
    .animation-end-delay {
      border: 2px solid currentColor;
      width: 48px;
    }

    .info {
      position: relative;
      display: flex;
      flex-direction: column;
      width: 2px;
      height: 2rem;
      margin: 0 1px;
    }

    .info + .info { margin-left: -3px; }

    .info-large { height: 4rem; }

    .info-top {
      bottom: 0;
      justify-content: flex-start;
      align-items: flex-end;
      align-self: flex-end;
    }

    .info-bottom {
      top: 0;
      justify-content: flex-end;
    }

    .info-bar {
      position: absolute;
      width: 2px;
      height: 100%;
      background-color: currentColor;
      transform-origin: 50% 100%;
    }

    .info-top .info-bar { bottom: 0; }
    .info-bottom .info-bar { top: 0; transform-origin: 50% 0%; }
    .info-left .info-bar { left: 0; }
    .info-right .info-bar { right: 0; }

    .info-label {
      white-space: nowrap;
      height: 1rem;
      padding-left: .5rem;
      padding-right: .5rem;
      line-height: 1rem;
    }

    .info-top .info-label {
      margin: -4px 0 0;
    }

    .info-bottom .info-label {
      margin: 0 0 -4px 0;
    }

  </style>
</head>
<body>

  <section class="feature-section feature-section-center feature-control" style="margin-top: 1000px; margin-bottom: 1000px;">

    <h1 class="section-title">
      <span>Time</span> <span>control</span> <span>& callbacks</span>
    </h1>

    <p class="section-description">
      Play, pause, control, reverse and trigger events in sync
    </p>

    <div class="time-control feature-anim">
      <div class="ruller">
        <div class="time-cursor color-red">
          <input value="999" />
        </div>
      </div>
      <div class="timeline">
        <div class="info-loop-begin info info-large info-left info-bottom" data-delay="0">
          <div class="info-bar"></div>
          <div class="info-label">loop begin</div>
          <div class="info-label">animation begin</div>
        </div>
        <div class="animation">
          <div class="animation-band animation-delay"></div>
          <div class="info-change-begin info info-left info-bottom" data-delay="100">
            <div class="info-bar"></div>
            <div class="info-label">change begin</div>
          </div>
          <div class="animation-band animation-change"></div>
          <div class="info-change-complete info info-right info-top" data-delay="400">
            <div class="info-bar"></div>
            <div class="info-label">change complete</div>
          </div>
          <div class="animation-band animation-end-delay"></div>
        </div>
        <div class="info-loop-complete info info-large info-right info-top" data-delay="500">
          <div class="info-bar"></div>
          <div class="info-label">loop complete</div>
        </div>
        <div class="info info-large info-left info-bottom" data-delay="500">
          <div class="info-bar"></div>
          <div class="info-label">loop begin</div>
          <div class="info-label">reverse</div>
        </div>
        <div class="animation">
          <div class="animation-band animation-delay"></div>
          <div class="info-change-begin info info-left info-bottom" data-delay="600">
            <div class="info-bar"></div>
            <div class="info-label">change begin</div>
          </div>
          <div class="animation-band animation-change"></div>
          <div class="info-change-complete info info-right info-top" data-delay="900">
            <div class="info-bar"></div>
            <div class="info-label">change complete</div>
          </div>
          <div class="animation-band animation-end-delay"></div>
        </div>
        <div class="info-loop-complete info info-large info-right info-top" data-delay="1000">
          <div class="info-bar"></div>
          <div class="info-label">loop complete</div>
        </div>
        <div class="info info-large info-left info-bottom" data-delay="1000">
          <div class="info-bar"></div>
          <div class="info-label">loop begin</div>
          <div class="info-label">reverse</div>
        </div>
        <div class="animation">
          <div class="animation-band animation-delay"></div>
          <div class="info-change-begin info info-left info-bottom" data-delay="1100">
            <div class="info-bar"></div>
            <div class="info-label">change begin</div>
          </div>
          <div class="animation-band animation-change"></div>
          <div class="info-change-complete info info-right info-top" data-delay="1400">
            <div class="info-bar"></div>
            <div class="info-label">change complete</div>
          </div>
          <div class="animation-band animation-end-delay"></div>
        </div>
        <div class="info-loop-complete info info-large info-right info-top" data-delay="1500">
          <div class="info-bar"></div>
          <div class="info-label">animation complete</div>
          <div class="info-label">loop complete</div>
        </div>
      </div>
    </div>

  </section>

</body>
<script type="module">

  import anime from '../../src/index.js';

  const timeControlEl = document.querySelector('.time-control');
  const rullerEl = document.querySelector('.ruller');
  const timeEl = document.querySelector('.time-cursor input');
  const infoEls = document.querySelectorAll('.info');
  const fragment = document.createDocumentFragment();
  const numberOfElements = 136;

  function isElementInViewport(el, inCB, outCB) {
    function handleIntersect(entries, observer) {
      var entry = entries[0];
      if (entry.isIntersecting) {
        if (inCB) inCB(el, entry);
      } else {
        if (outCB) outCB(el, entry);
      }
    }
    var observer = new IntersectionObserver(handleIntersect);
    observer.observe(el);
  }

  for (let i = 0; i < numberOfElements; i++) {
    const dotEl = document.createElement('div');
    dotEl.classList.add('line');
    fragment.appendChild(dotEl);
  }

  rullerEl.appendChild(fragment);

  const timeControlAnimation = anime.timeline({
    easing: 'linear',
    autoplay: false
  })
  .add({
    targets: '.time-cursor',
    keyframes: [
      { translateY: ['-1rem', 0], duration: 100, easing: 'easeInQuad' },
      { translateX: 'calc(34rem - 6px)', duration: 1500 },
      { translateY: '-1rem', duration: 100, easing: 'easeOutQuad' }
    ],
    duration: 1500,
    update: function(anim) {
      timeEl.value = anim.currentTime;
    }
  }, -100)
  .add({
    targets: '.ruller .line',
    translateY: [ {value: '1rem'}, {value: '0rem'} ],
    duration: 200,
    delay: anime.stagger([0, 1500]),
    easing: 'easeInOutSine'
  }, -100)

  for (var i = 0; i < infoEls.length; i++) {
    var infoEl = infoEls[i];
    var delay = parseFloat(anime.get(infoEl, 'data-delay'));
    var direction = infoEl.classList.contains('info-bottom') ? -1 : 1;
    timeControlAnimation
    .add({
      targets: infoEl.querySelector('.info-bar'),
      scaleY: [0, 1],
      duration: 250,
      easing: 'easeOutCirc'
    }, delay)
    .add({
      targets: infoEl.querySelectorAll('.info-label'),
      opacity: [0, 1],
      translateY: [direction * 10, 0],
      duration: 150,
      delay: anime.stagger(50, {start: 10, direction: direction > 0 ? 'reverse' : 'normal'}),
      easing: 'easeOutSine'
    }, delay)
  }

  var controlAnimationCanMove = false;

  function moveControlAnimation() {
    var rect = timeControlEl.getBoundingClientRect();
    var top = rect.top;
    var height = rect.height;
    var windowHeight = window.innerHeight;
    var scrolled = (top - windowHeight) * -1;
    timeControlAnimation.seek((scrolled * 3) - 500);
    if (controlAnimationCanMove) requestAnimationFrame(moveControlAnimation);
  }

  isElementInViewport(timeControlEl, function(el, entry) {
    controlAnimationCanMove = true;
    requestAnimationFrame(moveControlAnimation);
  }, function(el, entry) {
    controlAnimationCanMove = false;
  });

</script>
</html>
